#include "rasterizer.h"
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <stdint.h>

RRRasterizer::RRRasterizer()
{
  points_index = 0;
}

float TriangleArea(int xa, int ya, int xb, int yb, int xc, int yc)
{
  return 0.5 * abs((xa - xc) * (yb - yc) - (xb - xc) * (ya - yc));
}

void RRRasterizer::rrTriangleInterpolation(int X, int Y)
{
  RRRasterPoint pa, pb, pc;
  int xa, ya, xb, yb, xc, yc;
  float Xbc, Xac, Xab, abc;
  float a, b, c;
  pa = prim.rrGetPoint(current_triangle[0]);
  pb = prim.rrGetPoint(current_triangle[1]);
  pc = prim.rrGetPoint(current_triangle[2]);
  xa = pa.x; ya = pa.y;
  xb = pb.x; yb = pb.y;
  xc = pc.x; yc = pc.y;
  Xbc = TriangleArea(X, Y, xb, yb, xc, yc);
  Xac = TriangleArea(X, Y, xa, ya, xc, yc);
  Xab = TriangleArea(X, Y, xa, ya, xb, yb);
  abc = TriangleArea(xa, ya, xb, yb, xc, yc);
  a = Xbc / abc;
  b = Xac / abc;
  c = Xab / abc;
  // ただ内挿するだけ。depth考慮に入れてないので後で修正
  for(int i=0; i<4; i++) {
    points[points_index].color[i] = a * pa.color[i] + b * pb.color[i] + c * pc.color[i];
  }
}  

void RRRasterizer::rrAddPoint(int X, int Y)
{
  points[points_index].x = X;
  points[points_index].y = Y;
  RRRasterPoint pa, pb, pc;
  if(prim.rrGetType() == RR_LINE) {
    pa = prim.rrGetPoint(0);
    pb = prim.rrGetPoint(1);    
    int xa = pa.x; int ya = pa.y;
    int xb = pb.x; int yb = pb.y;
    float t;
    t = ((float)((X - xa) * (xb - xa) + (Y - ya) * (yb - ya))) / ((xb - xa) * (xb - xa) + (yb - ya) * (yb - ya));
    //float f;
    // ただ内挿するだけ。depth考慮に入れてないので後で修正
    for(int i=0; i<4; i++) {
      //printf("%d %d\n", pb.x, pb.y);
      points[points_index].color[i] = (1 - t) * pa.color[i] + t * pb.color[i];
      //printf("%f\n", points[points_index].color[i]);
    }
  }
  else if(prim.rrGetType() == RR_TRIANGLE) {
    rrTriangleInterpolation(X, Y);
  }
  else if(prim.rrGetType() == RR_POLYGON) {
    rrTriangleInterpolation(X, Y);
  }
  points_index++;
}

void RRRasterizer::rrRast()
{
  if(prim.rrGetType() == RR_LINE)
    rrLineRast(0, 1);
  else if(prim.rrGetType() == RR_LINE_STRIP)
    rrLineStripRast();
  else if(prim.rrGetType() == RR_LINE_LOOP)
    rrLineLoopRast();
  else if(prim.rrGetType() == RR_TRIANGLE)
    rrTriangleRast(0, 1, 2);
  else if(prim.rrGetType() == RR_POLYGON)
    rrPolygonRast();
}

void RRRasterizer::rrSetPrim(RRRasterPrim &arg)
{
  prim = arg;
  points_index = 0;
}

void RRRasterizer::rrLineRast(int s, int t)
{
  int x0, y0, x1, y1;
  x0 = prim.rrGetX(s);
  y0 = prim.rrGetY(s);
  x1 = prim.rrGetX(t);
  y1 = prim.rrGetY(t);
  
  int X, Y;
  X = x0;
  Y = y0;
  
  int dx, dy;
  dx = x1 - x0;
  dy = y1 - y0;

  int dirX, dirY;		/* 増分方向とその量 */
  dirX = 1;
  if(dx < 0) {
    dirX = -dirX;
    dx = -dx;
  }
  dirY = 1;
  if(dy < 0) {
    dirY = -dirY;
    dy = -dy;
  }

  int d, e, a, b, ax, ay, bx, by; /* 補正の設定 */
  if(dx < dy) {
    d = dy;
    e = -dy;
    a = 2 * dx;
    b = -2 * dy;
    ax = 0;
    ay = dirY;
    bx = dirX;
    by = 0;
  }
  else {
    d = dx;
    e = -dx;
    a = 2 * dy;
    b = -2 * dx;
    ax = dirX;
    ay = 0;
    bx = 0;
    by = dirY;
  }
  
  for(int i=0; i<=d; i++) {
    rrAddPoint(X, Y);
    X = X + ax;
    Y = Y + ay;
    e = e + a;
    if(e >= 0) {
      X = X + bx;
      Y = Y + by;
      e = e + b;
    }
  }
  
  //ラスタ座標の表示
  /* for(int i=0; i<points_index; i++) { */
  /*   printf("%d %d\n", points[i].x, points[i].y); */
  /* } */
}

int RRRasterizer::rrGetPointsNum()
{
  return points_index;
}

RRPoint RRRasterizer::rrGetPoint(int index)
{
  return points[index];
}

void RRRasterizer::rrLineStripRast()
{
  int vert_num = prim.rrGetVertsNum();
  for(int i=0; i<vert_num-1; i++) {
    rrLineRast(i, i+1);
  }
}

void RRRasterizer::rrLineLoopRast()
{
  int vert_num = prim.rrGetVertsNum();
  int i;
  for(i=0; i<vert_num-1; i++) {
    rrLineRast(i, i+1);
  }
  rrLineRast(i, 0);
}

template <class T>
inline void swap(T& a, T& b)
{
  T tmp(a);
  a = b;
  b = tmp;
}

void RRRasterizer::rrHLineRast(int x0, int x1, int Y)
{
  if(x1 < x0)
    swap(x0, x1);
  int dx;
  dx = x1 - x0;
  for(int i=0; i<=dx; i++) {
    rrAddPoint(x0 + i, Y);
  }
}

void RRRasterizer::rrTriangleRast(int s, int t, int u)
{
  current_triangle[0] = s;
  current_triangle[1] = t;
  current_triangle[2] = u;

  int x0, y0, x1, y1, x2, y2;
  x0 = prim.rrGetX(s);
  y0 = prim.rrGetY(s);
  x1 = prim.rrGetX(t);
  y1 = prim.rrGetY(t);
  x2 = prim.rrGetX(u);
  y2 = prim.rrGetY(u);
  
  if(y1 < y0) {
    swap(x1, x0);
    swap(y1, y0);
  }
  if(y2 < y0) {
    swap(x2, x0);
    swap(y2, y0);
  }
  if(y2 < y1) {
    swap(x2, x1);
    swap(y2, y1);
  }

  int Y;
  Y = y0;

  /* 長辺 */
  int dy2, dx2;
  dy2 = y2 - y0;
  if(dy2 <= 0) return;		/* つぶれてる */
  dx2 = x2 - x0;
  int mx2;
  if(dx2 >= 0)
    mx2 = dx2 / dy2;
  else
    mx2 = (dx2 - dy2 + 1) / dy2;
  int ax2, bx2, ex2;
  ax2 = 2 * (mx2 * dy2 - dx2);
  bx2 = 2 * dy2;
  ex2 = dy2;
  int hx2;
  hx2 = x0;

  /* 下半分 */
  int dy1, dx1;
  int mx1;
  int ax1, bx1, ex1;
  int hx1;
  dy1 = y1 - y0;
  if(dy1 > 0) {			/* 下半分がある */
    dx1 = x1 - x0;
    if(dx1 >= 0)
      mx1 = dx1 / dy1;
    else
      mx1 = (dx1 - dy1 + 1) / dy1;
    ax1 = 2 * (mx1 * dy1 - dx1);
    bx1 = 2 * dy1;
    ex1 = dy1;
    hx1 = x0;
    while(dy1 > 0) {
      rrHLineRast(hx1, hx2, Y);
      Y = Y + 1;
      hx2 = hx2 + mx2;
      ex2 = ex2 + ax2;
      if(ex2 <= 0) {
	hx2 = hx2 + 1;
	ex2 = ex2 + bx2;
      }
      hx1 = hx1 + mx1;
      ex1 = ex1 + ax1;
      if(ex1 <= 0) {
	hx1 = hx1 + 1;
	ex1 = ex1 + bx1;
      }
      dy1 = dy1 - 1;
    }
  }

  /* 上半分 */
  dy1 = y2 - y1;
  if(dy1 > 0) {
    dx1 = x2 - x1;
    if(dx1 >= 0)
      mx1 = dx1 / dy1;
    else
      mx1 = (dx1 - dy1 + 1) / dy1;
    ax1 = 2 * (mx1 * dy1 - dx1);
    bx1 = 2 * dy1;
    ex1 = dy1;
    hx1 = x1;
    while(dy1 > 0) {
      rrHLineRast(hx1, hx2, Y);
      Y = Y + 1;
      hx2 = hx2 + mx2;
      ex2 = ex2 + ax2;
      if(ex2 <= 0) {
	hx2 = hx2 + 1;
	ex2 = ex2 + bx2;
      }
      hx1 = hx1 + mx1;
      ex1 = ex1 + ax1;
      if(ex1 <= 0) {
	hx1 = hx1 + 1;
	ex1 = ex1 + bx1;
      }
      dy1 = dy1 - 1;
    }
  }
}

void RRRasterizer::rrPolygonRast()
{
  int verts_num;
  verts_num = prim.rrGetVertsNum();

  for(int i=0; i<verts_num-2; i++) {
    rrTriangleRast(0, i+1, i+2);
  }
}
